import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import copy

#初始化个体矩阵，zhujiao*banji 的二维数组
def initialize(banji,zhujiao):
    dividualData = []
    for i in range(zhujiao):
        preZJ = [0]
        preZJ = preZJ*banji
        dividualData.append(preZJ)
    randomList = []
    for i in range(banji):
        randomList.append(i)

    random.shuffle(randomList)

    for i, j in zip(range(banji), randomList):
        dividualData[i % zhujiao][j] = 1

    return dividualData

#求每一个助教修改的试卷份数,如果差值小于3，返回期望误差列表，如果大于3，返回空列表
def diffValue(sData,preZJ,banji,average_fen,deviation):
    yuejuan = 0
    currStopVariation = []
    for i in range(banji):
        if preZJ[i] == 1:
            currStopVariation.append(i)
            yuejuan += sData[i][1]

    #print("tempStopVariation",type(currStopVariation),currStopVariation)
    if abs(yuejuan - average_fen) <= deviation:
        return yuejuan,currStopVariation
    else:
        return yuejuan,[]

#目标函数，求个体当前每个助教修改的试卷份数和平均值的差值绝对值和
def mubiao(sData,individualData,banji,zhujiao,average_fen,deviation,stopVariation):
    allDValue = 0
    currStopVariation = copy.deepcopy(stopVariation)
    for i in range(zhujiao):
        currZJValue,tempStopVariation = diffValue(sData,individualData[i],banji,average_fen,deviation)
        for v in tempStopVariation:
            if v in currStopVariation:
                currStopVariation.remove(v)
        allDValue += pow((currZJValue - average_fen),2)
    return allDValue,currStopVariation

#计算适应度，群体中20个个体的差值绝对值和,以及每个个体当前的适应度
def shiyingdu(sData,allData,banji,zhujiao,average_fen,deviation,allStopVariation):
    syd = []
    currAllStopVariation = copy.deepcopy(allStopVariation)
    myallData = copy.deepcopy(allData)
    for i in range(20):
        currSYD = []
        currSYD.append(i)
        currDiviData,currAllStopVariation[i] = mubiao(sData,myallData[i],banji,zhujiao,average_fen,deviation,currAllStopVariation[i])
        currSYD.append(currDiviData)
        syd.append(currSYD)
    return syd,currAllStopVariation

#对适应度进行排序，由小到大，确定最优解的顺序
def sortSYD(syd):
    sydArr = np.array(syd)
    currSYD =  sydArr[sydArr[:,1].argsort()]
    return currSYD.tolist()

#判断交换列是否合法,col_1,col_2分别表示individ_1,individual_2的两个列
def isLegal(individual_1,individual_2,col_1,col_2,zhujiao,banji):
    row_1 = 0
    row_2 = 0
    ex_1A = 0
    ex_1B = 0
    ex_2A = 0
    ex_2B = 0
    #row_1,row_2 分别表示individual_1,individual_2选中的那一列中1的位置，即判断对应的1所在的行的位置
    for i in range(zhujiao):
        if individual_1[i][col_1] == 1:
            row_1 = i
        if individual_2[i][col_2] == 1:
            row_2 = i
    for j in range(banji):
        if individual_1[row_1][j] == 1:
            ex_1A += 1
        if individual_1[row_2][j] == 1:
            ex_1B += 1
        if individual_2[row_1][j] == 1:
            ex_2A += 1
        if individual_2[row_2][j] == 1:
            ex_2B += 1

    ex_1A -= 1
    ex_1B += 1
    ex_2A += 1
    ex_2B -= 1

    if (ex_1A >= 4 and ex_1A <= 6) and (ex_1B >= 4 and ex_1B <= 6)and(ex_2A >= 4 and ex_2A <= 6)and(ex_2B >= 4 and ex_2B <= 6):
        return True
    else:
        return False


#轮盘，如果新的总群产生更好地个体，替换bestDividual，和bestValue，并且将最好的5个个体复制4遍放到新的总群里面
def lunpan(syd,population,sData,banji,zhujiao,average_fen,bestDividual,bestValue,deviation,allStopVariation):
    mysyd = copy.deepcopy(syd)
    currSYD = sortSYD(mysyd)
    currBestValue = copy.deepcopy(bestValue)
    currBestDividual = copy.deepcopy(bestDividual)

    #currDivi = mubiao(sData,population[currSYD[0][0]],banji,zhujiao,average_fen,deviation,allStopVariation[currSYD[0][0]])[0]
    currDivi = currSYD[0][1]
    if currDivi < currBestValue:
        currBestValue = currDivi
        currBestDividual = population[currSYD[0][0]]

    newPopulation = population

    return newPopulation,currBestDividual,currBestValue

#交换，种群的不同个体之间互换列
def jiaohuan(population,banji,zhujiao,allStopVariation):
    newPopulation = copy.deepcopy(population)
    for i in range(5):
        divi_1 = random.randrange(20)
        divi_2 = random.randrange(20)

        col_1 = random.sample(allStopVariation[divi_1],1)[0]
        col_2 = random.sample(allStopVariation[divi_2],1)[0]
        if isLegal(newPopulation[divi_1],newPopulation[divi_2],col_1,col_2,zhujiao,banji) == True:
            #print("run this")
            for row in range(zhujiao):
                temp = newPopulation[divi_1][row][col_1]
                newPopulation[divi_1][row][col_1] = newPopulation[divi_2][row][col_2]
                newPopulation[divi_2][row][col_2] = temp

    return newPopulation

#变异，自换行列
def bianyi(population,banji,zhujiao,allStopVariation):
    newPopulation = copy.deepcopy(population)
    for i in range(25):
        divi = random.randrange(20)

        if allStopVariation[divi] != []:
            col_1 = random.sample(allStopVariation[divi],1)[0]
            col_2 = random.sample(allStopVariation[divi],1)[0]
            for row in range(zhujiao):
                temp = newPopulation[divi][row][col_1]
                newPopulation[divi][row][col_1] = newPopulation[divi][row][col_2]
                newPopulation[divi][row][col_2] = temp
    return newPopulation

#将结果绘图显示出来
def drawing(drawListX,drawListY):
    plt.figure()
    plt.suptitle('试卷分配')
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
    plt.xlabel('迭代次数')
    plt.ylabel('误差平方和')
    plt.plot(drawListX, drawListY, 'r', label='broadcast')
    plt.show()

if __name__ == '__main__':
    filePath = 'E:\code\python\EA\examination.csv'
    sData = pd.read_csv(filePath)
    sData = np.array(sData).tolist()

    print(sData)
    count = 0  # 试卷的卷数
    for preList in sData:
        count += preList[1]

    banji = len(sData) #82个班级
    zhujiao = 16 #16个助教
    average_fen = count // zhujiao #平均每个助教修改的试卷数

    #初始化一个个体，并复制20份，形成一个种群
    population = []
    for i in range(20):
        tenpIndividual = initialize(banji,zhujiao)
        population.append(tenpIndividual)


    #定义一个个体用来存储最优解,
    bestValue = 65535
    bestDividual = []
    for i in range(zhujiao):
        preZJ = [1]
        preZJ = preZJ*banji
        bestDividual.append(preZJ)

    #迭代2000次
    N = 1000

    deviation = 3  #如果某一个助教和平均值相差x以内，这些列不做变化
    stopVariation = []
    for i in range(banji):
        stopVariation.append(i)
    allStopVariation = [stopVariation]
    allStopVariation = allStopVariation*20

    drawListX = []
    drawListY = []

    for i in range(N):
        syd,allStopVariation = shiyingdu(sData,population,banji,zhujiao,average_fen,deviation,allStopVariation)
        print("第%d轮迭代，适应度为：" % i, syd)
        population,bestDividual,bestValue = lunpan(syd,population,sData,banji,zhujiao,average_fen,bestDividual,bestValue,deviation,allStopVariation)
        drawListX.append(i)
        drawListY.append(bestValue)
        #population = jiaohuan(population,banji,zhujiao,allStopVariation)
        population = bianyi(population,banji,zhujiao,allStopVariation)

    print("最终解误差平方和:", bestValue)
    for preZJ in bestDividual:
        preBJ = []
        ti = 0
        for bj in preZJ:
            if bj == 1:
                preBJ.append(ti)
            ti += 1

        print(deviation, average_fen, abs(diffValue(sData, preZJ, banji, average_fen, deviation)[0] - average_fen),
              diffValue(sData, preZJ, banji, average_fen, deviation)[0], preBJ)

    drawing(drawListX, drawListY)


